﻿using System;
using System.Collections.Generic;
using System.Numerics;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// Convert类型扩展方法类
    /// </summary>
    public static partial class ConvertEx
    {

        private static Dictionary<Type, ITypeConverter> _typeConverterDic = null;
        private static void InitTypeConverter()
        {
            var typeConverterDic = new Dictionary<Type, ITypeConverter>();

            //typeConverterDic.Add(typeof(BigInteger), new BigIntegerTypeConverter());
            //typeConverterDic.Add(typeof(Boolean), new BooleanTypeConverter());
            //typeConverterDic.Add(typeof(Byte), new ByteTypeConverter());
            //typeConverterDic.Add(typeof(Char), new CharTypeConverter());
            //typeConverterDic.Add(typeof(DateOnly), new DateOnlyTypeConverter());

            //typeConverterDic.Add(typeof(DateTimeOffset), new DateTimeOffsetTypeConverter());
            //typeConverterDic.Add(typeof(DateTime), new DateTimeTypeConverter());
            //typeConverterDic.Add(typeof(Decimal), new DecimalTypeConverter());
            //typeConverterDic.Add(typeof(Double), new DoubleTypeConverter());
            //typeConverterDic.Add(typeof(Enum), new EnumTypeConverter());

            //typeConverterDic.Add(typeof(Int16), new Int16TypeConverter());
            //typeConverterDic.Add(typeof(Int32), new Int32TypeConverter());
            //typeConverterDic.Add(typeof(Int64), new Int64TypeConverter());
            //typeConverterDic.Add(typeof(SByte), new SByteTypeConverter());
            //typeConverterDic.Add(typeof(Single), new SingleTypeConverter());

            //typeConverterDic.Add(typeof(String), new StringTypeConverter());
            //typeConverterDic.Add(typeof(TimeOnly), new TimeOnlyTypeConverter());
            //typeConverterDic.Add(typeof(UInt16), new UInt16TypeConverter());
            //typeConverterDic.Add(typeof(UInt32), new UInt32TypeConverter());
            //typeConverterDic.Add(typeof(UInt64), new UInt64TypeConverter());

            Type[] types = typeof(ConvertEx).Assembly.GetExportedTypes();
            Type iTypeConverterType = typeof(ITypeConverter);
            ITypeConverter converter, oldConverter;
            foreach (var type in types)
            {
                if (!type.IsClass || type.IsAbstract)
                {
                    continue;
                }

                if (type.GetInterface(iTypeConverterType.FullName) != null)
                {
                    converter = (ITypeConverter)Activator.CreateInstance(type);
                    if (typeConverterDic.TryGetValue(converter.ConverterType, out oldConverter))
                    {
                        throw new ApplicationException($"转换器\"{oldConverter.GetType().FullName}\"与\"{converter.GetType().FullName}\"针对的目标类型为同相同类型");
                    }
                    typeConverterDic.Add(converter.ConverterType, converter);
                }
            }

            _typeConverterDic = typeConverterDic;
        }

        private static bool TryGetTypeConverter(Type converterType, out ITypeConverter targetConverter)
        {
            if (_typeConverterDic == null)
            {
                InitTypeConverter();
            }

            if (converterType.IsEnum)
            {
                converterType = typeof(Enum);
            }

            return _typeConverterDic.TryGetValue(converterType, out targetConverter);
        }

        /// <summary>
        /// 添加类型转换器
        /// </summary>
        /// <param name="converter">类型转换器</param>
        /// <exception cref="ArgumentNullException">类型转换器为null将抛出异常</exception>
        public static void AddTypeConverter(ITypeConverter converter)
        {
            if (converter == null)
            {
                throw new ArgumentNullException(nameof(converter));
            }

            if (_typeConverterDic == null)
            {
                InitTypeConverter();
            }

            _typeConverterDic[converter.ConverterType] = converter;
        }



        #region 类型转换
        /// <summary>
        /// 转换数据到
        /// </summary>
        /// <typeparam name="T">clr类型</typeparam>
        /// <typeparam name="TResult">clr类型</typeparam>
        /// <param name="value">clr类型值</param>        
        /// <returns>数据库字段值</returns>
        public static TResult? ConvertTo<T, TResult>(T? value)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
            where TResult : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            if (value.HasValue)
            {
                return (TResult)ConvertToObject(typeof(TResult), value.Value);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 转换数据到
        /// </summary>
        /// <typeparam name="T">要待转换的目标类型</typeparam>
        /// <typeparam name="TResult">目标类型</typeparam>
        /// <param name="value">要转换的值</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static TResult ConvertTo<T, TResult>(T value)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
            where TResult : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            return (TResult)ConvertToObject(typeof(TResult), value);
        }

        /// <summary>
        /// 尝试转换数据到
        /// </summary>
        /// <typeparam name="T">要待转换的目标类型</typeparam>
        /// <typeparam name="TResult">目标类型</typeparam>
        /// <param name="value">要转换的值</param>
        /// <param name="result">结果值</param>
        /// <returns>转换结果</returns>
        public static bool TryConvertTo<T, TResult>(T value, out TResult result)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
            where TResult : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
#pragma warning disable IDE0018
            object obj;
            bool ret = TryConvertToObject(typeof(TResult), value, out obj);
            if (ret)
            {
                result = (TResult)obj;
            }
            else
            {
#pragma warning disable IDE0034
                result = default(TResult);
            }

            return ret;
        }

        /// <summary>
        /// 转换数据到
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static object ConvertToObject(Type targetValueType, object value)
        {
            if (targetValueType == null)
            {
                throw new ArgumentNullException(nameof(targetValueType));
            }

            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            object result;
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                if (converter.TryConvertTo(targetValueType, value, out result))
                {
                    return result;
                }
                else
                {
                    throw new ArgumentException($"\"{value}\"转换为类型\"{targetValueType.FullName}\"值失败");
                }
            }
            else
            {
                return Convert.ChangeType(value, targetValueType);
            }
        }

        /// <summary>
        /// 尝试转换数据到指定类型
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <param name="result">转换后的值</param>
        /// <returns></returns>
        public static bool TryConvertToObject(Type targetValueType, object value, out object result)
        {
            result = null;
            if (targetValueType == null)
            {
                throw new ArgumentNullException(nameof(targetValueType));
            }

            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                return converter.TryConvertTo(targetValueType, value, out result);
            }
            else
            {
                return false;
            }
        }
        #endregion





        #region ReadOnlySpan<char>转换为指定类型数值类型

#if CORE

        /// <summary>
        /// 字符串转换为指定类型数值类型
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">待转换字符串</param>
        /// <param name="style">字符串中数字样式</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>目标数值</returns>
        public static T ConvertTo<T>(ReadOnlySpan<char> value, object style = null, IFormatProvider formatProvider = null)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            return (T)ConvertTo(typeof(T), value, style, formatProvider);
        }

        /// <summary>
        /// 各种进制字符串转换为数据类型,转换失败返回默认值,包括枚举
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">要转换的值</param>
        /// <param name="defaultValue">转换失败时的默认值</param>
        /// <param name="style">字符串中数字样式</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static T ConvertTo<T>(ReadOnlySpan<char> value, T defaultValue, object style = null, IFormatProvider formatProvider = null)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            T resultValue;
            try
            {
                resultValue = (T)ConvertTo(typeof(T), value, style, formatProvider);
            }
            catch
            {
                resultValue = defaultValue;
            }

            return resultValue;
        }

        /// <summary>
        /// 将字符串转换为值类型数据,包括枚举
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <param name="style">字符串中数字样式</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static object ConvertTo(Type targetValueType, ReadOnlySpan<char> value, object style = null, IFormatProvider formatProvider = null)
        {
            if (targetValueType == null)
            {
                throw new ArgumentNullException(nameof(targetValueType));
            }

            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            object result;
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                if (converter.TryConvertTo(targetValueType, value, out result, style, formatProvider))
                {
                    return result;
                }
                else
                {
                    throw new ArgumentException($"\"{new string(value)}\"转换为类型\"{targetValueType.FullName}\"值失败");
                }
            }
            else
            {
                return Convert.ChangeType(new string(value), targetValueType);
            }
        }


        /// <summary>
        /// 尝试将字符串转换为值类型数据,包括枚举
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <param name="result">转换结果</param>
        /// <param name="style">字符串中数值样式[数字样式为NumberStyles值之一，日期时间为DateTimeStyles值之一,默认null为不使用样式]</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static bool TryConvertTo(Type targetValueType, ReadOnlySpan<char> value, out object result, object style = null, IFormatProvider formatProvider = null)
        {
            result = null;
            if (targetValueType == null)
            {
                return false;
            }

            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                return converter.TryConvertTo(targetValueType, value, out result, style, formatProvider);
            }
            else
            {
                string str = new string(value);
                try
                {
                    result = Convert.ChangeType(str, targetValueType);
                    return true;
                }
                catch (Exception ex)
                {
                    ZLog.ZLoger.Warn(ex, $"\"{str}\"转换为类型\"{targetValueType.FullName}\"值失败");
                    return false;
                }
            }
        }

#endif

        #endregion



        #region 字符串转换为指定类型数值类型

        /// <summary>
        /// 字符串转换为指定类型数值类型
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">待转换字符串</param>
        /// <param name="style">字符串中数值样式[数字样式为NumberStyles值之一，日期时间为DateTimeStyles值之一,默认null为不使用样式]</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>目标数值</returns>
        public static T ConvertTo<T>(string value, object style = null, IFormatProvider formatProvider = null)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            return (T)ConvertTo(typeof(T), value, style, formatProvider);
        }

        /// <summary>
        /// 各种进制字符串转换为数据类型,转换失败返回默认值,包括枚举
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">要转换的值</param>
        /// <param name="defaultValue">转换失败时的默认值</param>
        /// <param name="style">字符串中数值样式[数字样式为NumberStyles值之一，日期时间为DateTimeStyles值之一,默认null为不使用样式]</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static T ConvertTo<T>(string value, T defaultValue, object style = null, IFormatProvider formatProvider = null)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            T resultValue;
            try
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    return defaultValue;
                }

                resultValue = (T)ConvertTo(typeof(T), value, style, formatProvider);
            }
            catch
            {
                resultValue = defaultValue;
            }

            return resultValue;
        }



        /// <summary>
        /// 将字符串转换为值类型数据,包括枚举
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <param name="style">字符串中数值样式[数字样式为NumberStyles值之一，日期时间为DateTimeStyles值之一,默认null为不使用样式]</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static object ConvertTo(Type targetValueType, string value, object style = null, IFormatProvider formatProvider = null)
        {
            if (targetValueType == null)
            {
                throw new ArgumentNullException(nameof(targetValueType));
            }

            object result;
            ITypeConverter converter;
            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                if (converter.TryConvertTo(targetValueType, value, out result, style, formatProvider))
                {
                    return result;
                }
                else
                {
                    throw new ArgumentException($"\"{value}\"转换为类型\"{targetValueType.FullName}\"值失败");
                }
            }
            else
            {
                return Convert.ChangeType(value, targetValueType);
            }

        }

        /// <summary>
        /// 尝试将字符串转换为值类型数据,包括枚举
        /// </summary>
        /// <param name="targetValueType">要待转换的目标类型</param>
        /// <param name="value">要转换的值</param>
        /// <param name="result">转换结果</param>
        /// <param name="style">字符串中数值样式[数字样式为NumberStyles值之一，日期时间为DateTimeStyles值之一,默认null为不使用样式]</param>
        /// <param name="formatProvider">一个提供区域性特定的格式设置信息的对象</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static bool TryConvertTo(Type targetValueType, string value, out object result, object style = null, IFormatProvider formatProvider = null)
        {
            result = null;
            if (targetValueType == null)
            {
                return false;
            }

            targetValueType = TypeEx.GetPrimitiveType(targetValueType);
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                return converter.TryConvertTo(targetValueType, value, out result, style, formatProvider);
            }
            else
            {
                try
                {
                    result = Convert.ChangeType(value, targetValueType);
                    return true;
                }
                catch (Exception ex)
                {
                    ZLog.ZLoger.Warn(ex, $"\"{value}\"转换为类型\"{targetValueType.FullName}\"值失败");
                    return false;
                }
            }
        }
        #endregion




        #region 带进制格式的字符串转换为指定类型数值类型
        /// <summary>
        /// 字符串转换为指定类型数值类型
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">待转换字符串</param>
        /// <param name="fromBase">值中数字基数(value是何种进制的字符串),必须是2,8,10,16</param>
        /// <returns>目标数值</returns>
        public static T ConvertTo<T>(string value, byte fromBase = 10)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase == 16)
            {
                throw new ArgumentException("值中数字基数(value是何种进制的字符串),必须是2,8,10,16", nameof(fromBase));
            }

            Type targetValueType = typeof(T);
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                return (T)converter.ConvertTo(targetValueType, value, fromBase);
            }
            else
            {
                throw new ArgumentException($"类型转换器\"{targetValueType.FullName}\"对应的转换器不存在");
            }
        }

        /// <summary>
        /// 各种进制字符串转换为数据类型,转换失败返回默认值,包括枚举
        /// </summary>
        /// <typeparam name="T">目标数值类型</typeparam>
        /// <param name="value">要转换的值</param>
        /// <param name="defaultValue">转换失败时的默认值</param>
        /// <param name="fromBase">值中数字基数(value是何种进制的字符串),必须是2,8,10,16</param>
        /// <returns>转换后的值,存放在object中,如果转换失败为目标类型的默认值</returns>
        public static T ConvertTo<T>(string value, T defaultValue, byte fromBase = 10)
            where T : struct, IConvertible, IFormattable, IComparable<T>, IEquatable<T>
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase == 16)
            {
                throw new ArgumentException("值中数字基数(value是何种进制的字符串),必须是2,8,10,16", nameof(fromBase));
            }

            Type targetValueType = typeof(T);
            ITypeConverter converter;
            if (TryGetTypeConverter(targetValueType, out converter))
            {
                return (T)converter.ConvertTo(targetValueType, value, fromBase);
            }
            else
            {
                return defaultValue;
            }
        }


        #endregion

    }
}
